// 该类是对文件描述符需要进行IO的事件管理的模块，实现对描述符可读，可写，错误等事件的管理操作
#pragma once
#include "Log.hpp"

/*
 * EventLoop 包含多个Channel 和 一个Poller -> 多路分发器
 * Channel 通道 封装了sockfd和其感兴趣的event，如EPOLLIN、EPOLLOUT事件
 * 还绑定了poller返回的具体事件
 */
class EventLoop;
class Channel
{
private:
    int _fd;           // Poller监听的对象
    EventLoop *_loop;  // 事件循环
    uint32_t _events;  // 注册fd感兴趣的事件
    uint32_t _revents; // poller返回的具体发生的事件
    using EventCallback = std::function<void()>;
    // Channel获得了fd可以发生的revents，因此可以绑定回调函数
    EventCallback _read_callback;  // 可读事件被触发的回调函数
    EventCallback _write_callback; // 可写事件被触发的回调函数
    EventCallback _error_callback; // 错误事件被触发的回调函数
    EventCallback _close_callback; // 连接断开事件被触发的回调函数
    EventCallback _event_callback; // 任意事件被触发的回调函数

public:
    Channel(EventLoop *loop, int fd) : _fd(fd), _events(0), _revents(0), _loop(loop) {}

    int Fd() { return _fd; }

    // 获取想要监控的事件
    uint32_t Events() { return _events; } 

    // 设置实际就绪的事件
    void SetREvents(uint32_t events) { _revents = events; } 

    // 设置回调函数对象
    void SetReadCallback(const EventCallback &cb) { _read_callback = std::move(cb); }
    void SetWriteCallback(const EventCallback &cb) { _write_callback = std::move(cb); }
    void SetErrorCallback(const EventCallback &cb) { _error_callback = std::move(cb); }
    void SetCloseCallback(const EventCallback &cb) { _close_callback = std::move(cb); }
    void SetEventCallback(const EventCallback &cb) { _event_callback = std::move(cb); }

    // 设置fd相应的事件状态
    // 启动读事件监控
    void EnableRead() { _events |= EPOLLIN; Update(); }
    // 关闭读事件监控
    void DisableRead() { _events &= ~EPOLLIN; Update(); }
    // 启动写事件监控
    void EnableWrite() { _events |= EPOLLOUT; Update(); }
    // 关闭写事件监控
    void DisableWrite() { _events &= ~EPOLLOUT; Update(); }
    // 关闭所有事件监控
    void DisableAll() { _events = 0; Update(); }

    // 返回当前fd的事件状态
    // 当前是否监控了可读
    bool ReadAble() { return (_events & EPOLLIN); }
    // 当前是否监控了可写
    bool WriteAble() { return (_events & EPOLLOUT); }

    // 移除监控
    void Remove();
    // 向Poller中更改fd设置的事件 但是只能通过EventLoop执行
    void Update();

    // fd得到poller通知以后，处理事件调用相应的回调方法
    void HandleEvent()
    {
        // 处理可读事件
        // 第一参数，有数据可读；第二参数，对方关闭连接；第三参数，带外数据
        if ((_revents & EPOLLIN) || (_revents & EPOLLRDHUP) || (_revents & EPOLLPRI))
        {
            if (_read_callback)
                _read_callback();
        }

        // 处理可写事件
        /*有可能会释放连接的操作事件,一次只处理一个*/
        if (_revents & EPOLLOUT)
        {
            if (_write_callback)
                _write_callback();
        }
        // 处理错误事件
        else if (_revents & EPOLLERR)
        {
            if (_error_callback)
                _error_callback();
        }
        // 处理连接关闭事件
        else if (_revents & EPOLLHUP)
        {
            if (_close_callback)
                _close_callback();
        }
        
        if (_event_callback)
            _event_callback();
    }
};

